package cn.yogehaoren.badfox.struct;


import cn.yogehaoren.badfox.annotation.validate.NotNull;
import cn.yogehaoren.badfox.core.BadFox;
import cn.yogehaoren.badfox.http.BadFoxCookie;
import cn.yogehaoren.badfox.http.HttpMethod;
import okhttp3.HttpUrl;

import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class Request extends CommonObject {

    @FunctionalInterface
    public static interface BadFoxCallbackFunc<T,A1>{
        /**
         *
         * @param t CrawlerModel
         * @param a1 Response
         */
        void call(T t, A1 a1);
    }

    public <T,A1> Request(String url, String callBack, BadFoxCallbackFunc<T,A1> cbFunc, HttpMethod httpMethod, Map<String, String> params, Map<String, Object> meta, int maxReqCount) {
        this.url = url;
        this.httpMethod = httpMethod;
        this.params = params;
        this.meta = meta;
        this.callBack = callBack;
        this.maxReqCount = maxReqCount;
        this.callBackFunc = cbFunc;
        if (callBackFunc!=null){
            this.lambdaCb = true;
        }
    }

    public Request(String url,String callBack){
        this.url = url;
        this.callBack = callBack;
    }

    public <T,A1> Request(String url, BadFoxCallbackFunc<T,A1> callBackFunc){
        this.url = url;
        this.callBackFunc = callBackFunc;
        this.lambdaCb = true;
    }


    public static <T,A1> Request build(String url, String callBack, BadFoxCallbackFunc<T,A1> callBackFunc, HttpMethod httpMethod, Map<String, String> params, Map<String, Object> meta, int maxReqcount){
        return new Request(url, callBack,callBackFunc, httpMethod, params, meta, maxReqcount);
    }

    public static Request build(String url, String callBack, HttpMethod httpMethod, Map<String, String> params, Map<String, Object> meta){
        return new Request(url, callBack,null, httpMethod, params, meta,1);
    }

    public static Request build(String url, String callBack){
        return new Request(url, callBack,null,null,null,null,1);
    }

    public static <T ,A1> Request build(String url, BadFoxCallbackFunc<T,A1> callBackFunc){
        return new Request(url, null, callBackFunc,null,null,null,1);
    }

    public static Request build(String url, String callBack, int maxReqCount){
        return new Request(url, callBack,null,null,null,null, maxReqCount);
    }

    public Request(){
        super();
    }

    @NotNull
    private String crawlerName;
    /**
     * 需要请求的url
     */
    @NotNull
    private String url;
    /**
     * 要请求的方法类型 get,post,put...
     */
    private HttpMethod httpMethod;
    /**
     * 如果请求需要参数，那么将参数放在这里
     */
    private Map<String,String> params;
    /**
     * 这个主要用于存储向下级回调函数传递的一些自定义数据
     */
    private Map<String,Object> meta;
    /**
     * 回调函数方法名
     */
    @NotNull
    private String callBack;

    /**
     * 代理
     */
    private Proxy proxy;

    /**
     * 处理该请求后，处理器应该停顿的时间
     * -1: 表示该请求不单独设置延时
     */
    private int delay = -1;

    /**
     * 回调函数是否为Lambda表达式
     */
    private transient boolean lambdaCb = false;

    /**
     * 回调函数
     */
    private transient BadFoxCallbackFunc callBackFunc;
    /**
     * 是否停止的信号，收到该信号的处理线程会退出
     */
    private boolean stop = false;
    /**
     * 最大可被重新请求次数
     */
    private int maxReqCount = 3;

    /**
     * 用来记录当前请求被执行过的次数
     */
    private int currentReqCount = 0;

    /**
     * 用来指定一个请求是否要经过去重机制
     */
    private boolean skipDuplicateFilter = false;

	/**
     * 自定义Http请求协议头
     */
    private Map<String,String> header;


    /**
     * 支持添加自定义cookie
     */
    private List<BadFoxCookie> badFoxCookies;

    /**
     * 添加json request body支持
     */
    private String jsonBody;

    private boolean useReferer  = true;


    public boolean isUseReferer() {
        return useReferer;
    }

    public void setUseReferer(boolean useReferer) {
        this.useReferer = useReferer;
    }

    public void incrReqCount(){
        this.currentReqCount +=1;
    }

    public String getUrl() {
        return url;
    }

    public Request setUrl(String url) {
        this.url = url;
        return this;
    }

    public HttpMethod getHttpMethod() {
        return httpMethod;
    }

    public Request setHttpMethod(HttpMethod httpMethod) {
        this.httpMethod = httpMethod;
        return this;
    }

    public Map<String, String> getParams() {
        return params;
    }

    public Request setParams(Map<String, String> params) {
        this.params = params;
        return this;
    }

    public Map<String, Object> getMeta() {
        //保证用起来时可定不为空，方便使用
        if (meta == null){
            meta = new HashMap<>();
        }
        return meta;
    }

    public Request setMeta(Map<String, Object> meta) {
        this.meta = meta;
        return this;
    }

    public String getCallBack() {
        return callBack;
    }

    public Request setCallBack(String callBack) {
        this.callBack = callBack;
        return this;
    }

    public Request setCallBack(BadFoxCallbackFunc<BadFox, Response> cbFunc) {
        this.callBackFunc = cbFunc;
        this.lambdaCb = true;
        return this;
    }

    public boolean isStop() {
        return stop;
    }

    public Request setStop(boolean stop) {
        this.stop = stop;
        return this;
    }

    public int getMaxReqCount() {
        return maxReqCount;
    }

    public Request setMaxReqCount(int maxReqCount) {
        this.maxReqCount = maxReqCount;
        return this;
    }

    public int getCurrentReqCount() {
        return currentReqCount;
    }

    public Request setCurrentReqCount(int currentReqCount) {
        this.currentReqCount = currentReqCount;
        return this;
    }

    public boolean isSkipDuplicateFilter() {
        return skipDuplicateFilter;
    }

    public Request setSkipDuplicateFilter(boolean skipDuplicateFilter) {
        this.skipDuplicateFilter = skipDuplicateFilter;
        return this;
    }

    public String getCrawlerName() {
        return crawlerName;
    }

    public Request setCrawlerName(String crawlerName) {
        this.crawlerName = crawlerName;
        return this;
    }


    public int getDelay() {
        return delay;
    }

    public void setDelay(int delay) {
        this.delay = delay;
    }



    public String urlJoin(String path){

        HttpUrl base = HttpUrl.parse(url);
        return base.resolve(path).toString();

    }

    public Map<String, String> getHeader() {
        return header;
    }

    public Request setHeader(Map<String, String> header) {
        this.header = header;
        return this;
    }

    public List<BadFoxCookie> getCookies() {
        return badFoxCookies;
    }

    public Request setCookies(List<BadFoxCookie> badFoxCookies) {
        this.badFoxCookies = badFoxCookies;
        return this;
    }

    public BadFoxCallbackFunc getCallBackFunc() {
        return callBackFunc;
    }

    public boolean isLambdaCb() {
        return lambdaCb;
    }

    public String getJsonBody() {
        return jsonBody;
    }

    public void setJsonBody(String jsonBody) {
        this.jsonBody = jsonBody;
    }

    public Proxy getProxy() {
        return proxy;
    }

    public void setProxy(Proxy proxy) {
        this.proxy = proxy;
    }




    public Request addHeader(String name, String value){

        if(header == null){
            header = new HashMap<>();
        }
        header.put(name, value);
        return this;
    }

    public Request addCookie(BadFoxCookie cookie ){
        if(badFoxCookies == null){
            badFoxCookies = new ArrayList<>();
        }
        badFoxCookies.add(cookie);
        return this;
    }

    public Request addMeta(String key, Object value){
        if(meta == null){
            meta = new HashMap<>();
        }
        meta.put(key, value);
        return this;
    }

    public String getStringMeta(String key){
        if(meta == null || !meta.containsKey(key)){
            return "";
        }
        else {
            return String.valueOf(meta.get(key));
        }
    }

    public Double getDoubleMeta(String key){
        if(meta == null || !meta.containsKey(key)){
            return null;
        }else {
            if(meta.get(key) instanceof Double){
                return (Double)meta.get(key);
            }else {
                return null;
            }

        }
    }

    public Integer getIntegerMeta(String key){
        if(meta == null || !meta.containsKey(key)){
            return null;
        }else {
            if(meta.get(key) instanceof Integer){
                return (Integer) meta.get(key);
            }else {
                return null;
            }
        }
    }


    public Long getLongMeta(String key){
        if(meta == null || !meta.containsKey(key)){
            return null;
        }else {
            if(meta.get(key) instanceof Long){
                return (Long) meta.get(key);
            }else {
                return null;
            }
        }
    }

    public <T> T getMetadata(Class<T> clazz, String key){

        if(meta == null){
            return null;
        }
        else {

            Object object = meta.get(key);
            if(object == null || !object.getClass().isAssignableFrom(clazz)){
                return null;
            }else {
                return (T) object;
            }
        }

    }
}
